﻿using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace Dwarf.Managed
{
    /// <summary>Solve the MapReduce problem with TPL parallelization</summary>
    ///
    class Solver
    {
        private Configurator dwarfConfigurator;              // processed settings from command line
        private string mainContent;                          // content for parsing
        private SortedDictionary<String, int> stringTotal;         // final result Dictionary "word" - "count of entries"
        private MapReduceBaseMethod[] currentData;           // array of the solving method objects

        /// <summary>Construct the new solver</summary>
        /// <param name="dwarfConfigurator">Processed command line arguments</param>
        /// 
        public Solver(ref Configurator dwarfConfigurator)
        {
            this.dwarfConfigurator = dwarfConfigurator;
            dwarfConfigurator.WriteSettings();               // write settings to console and profile file
            mainContent = dwarfConfigurator.GetData();
            InitParallelization(dwarfConfigurator.GetThreadCount());
        }

        /// <summary>TPL-parallelized method MapReduce</summary>
        ///
        public void Solve()
        {
            Parallel.For(0, dwarfConfigurator.GetThreadCount(), delegate(int i)        // TPL directive
            {
                currentData[i].FunctionMap();                                          // execute Map
                currentData[i].FunctionReduce();                                       // execute Reduce
            });
            SumThreadResult(dwarfConfigurator.GetThreadCount());                       // results summarize
        }

        /// <summary>Init the configuration. Creating objects for parallel processing</summary>
        /// <param name="threadCount">Count of threads used for parallelization</param>
        ///
        private void InitParallelization(int threadCount)
        {
            currentData = new MapReduceBaseMethod[threadCount];
            int contentSize = mainContent.Length;
            int interval = 1 + contentSize / threadCount;   // partitioning the String content (same parts for all threads)
            int leftBoard = 0;
            int part = 0;
            while (part < threadCount - 1)
            {
                int certainInterval =  interval;
                while (mainContent[leftBoard + certainInterval] != ' ') // search of middle-word space
                {
                    certainInterval++;
                }
                certainInterval++;                                      // create the contents for all parallel methods
                currentData[part] = new MapReduceBaseMethod(mainContent.Substring(leftBoard, certainInterval));
                part++;
                leftBoard += certainInterval;
            }
            currentData[threadCount - 1] = new MapReduceBaseMethod(mainContent.Substring(leftBoard));
        }

        /// <summary>Summarize the results of threads</summary>
        /// <param name="threadCount">Count of threads used for parallelization</param>
        ///
        private void SumThreadResult(int threadCount)
        {
            try
            {
                Dictionary<string, int> unsortedStringTotal = currentData[0].GetStringTotal();
                for (int i = 1; i < threadCount; i++)                       // for each partial result
                {
                    foreach (var pair in currentData[i].GetStringTotal())
                    {
                        int value = pair.Value;
                        if (unsortedStringTotal.ContainsKey(pair.Key))              // check insertion of each key
                        {
                            value += unsortedStringTotal[pair.Key];                 // if inserted, add value and refresh 
                            unsortedStringTotal.Remove(pair.Key);
                        }
                        unsortedStringTotal.Add(pair.Key, value);
                    }
                }
                stringTotal = new SortedDictionary<string, int>(unsortedStringTotal);
            }
            catch (Exception e)
            {
                Console.WriteLine("Error: {0}", e.Message);
            }
        }

        /// <summary>Problem results output</summary>
        /// <param name="time">Spent time</param>
        ///
        public void Finish(double time)
        {
            dwarfConfigurator.Close(time, stringTotal);
        }
    }
}